package com.flyqiu.common.sc.statistical;

import com.flyqiu.common.property.FiyQiuMapProperty;
import com.flyqiu.common.property.FlyQiuBaseProperty;
import com.flyqiu.common.property.FlyQiuFieldProperty;
import com.flyqiu.common.property.FlyQiuPropertyDesc;
import com.flyqiu.common.sc.FlyQiuSCContext;
import com.flyqiu.common.sc.calculate.FlyQiuCalculate;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 * 属性统计类，用于对给定的属性进行统计计算
 *
 * @param <T> 属性所在的对象类型
 */
@Data
public class FlyQiuPropertyStatistical<T> implements IFlyQiuStatistical<T> {

    // 用于指定统计结果字符串的前缀
    protected String prefix = "";

    // 用于指定统计结果字符串的后缀
    protected String suffix = "";

    // 统计结果的精度
    protected int precision;

    // 用于分割多个属性的字符串
    protected String split;

    // 属性描述对象
    protected final FlyQiuBaseProperty field;

    // 统计计算对象
    protected final FlyQiuCalculate<? super Object> calculate;
    protected final String outFieldName;

    /**
     * 使用字段名称初始化统计类
     *
     * @param field     字段名称
     * @param calculate 统计计算逻辑
     */
    public FlyQiuPropertyStatistical(String field, FlyQiuCalculate<?> calculate) {
        this(new FiyQiuMapProperty(field), calculate);
    }

    /**
     * 使用字段名称和输出字段名称初始化统计类
     *
     * @param field        字段名称
     * @param outFieldName 输出字段名称
     * @param calculate    统计计算逻辑
     */
    public FlyQiuPropertyStatistical(String field, String outFieldName, FlyQiuCalculate<?> calculate) {
        this(new FiyQiuMapProperty(field), outFieldName, calculate);
    }

    /**
     * 使用反射Field对象初始化统计类
     *
     * @param field     反射Field对象
     * @param calculate 统计计算逻辑
     */
    public FlyQiuPropertyStatistical(Field field, FlyQiuCalculate<?> calculate) {
        this(new FlyQiuFieldProperty(field), calculate);
    }

    /**
     * 使用反射Field对象和输出字段名称初始化统计类
     *
     * @param field        反射Field对象
     * @param outFieldName 输出字段名称
     * @param calculate    统计计算逻辑
     */
    public FlyQiuPropertyStatistical(Field field, String outFieldName, FlyQiuCalculate<?> calculate) {
        this(new FlyQiuFieldProperty(field), outFieldName, calculate);
    }

    /**
     * 使用PropertyDescriptor描述属性初始化统计类
     *
     * @param pd        PropertyDescriptor对象
     * @param calculate 统计计算逻辑
     */
    public FlyQiuPropertyStatistical(PropertyDescriptor pd, FlyQiuCalculate<?> calculate) {
        this(new FlyQiuPropertyDesc(pd), calculate);
    }

    /**
     * 使用PropertyDescriptor描述属性和输出字段名称初始化统计类
     *
     * @param pd           PropertyDescriptor对象
     * @param outFieldName 输出字段名称
     * @param calculate    统计计算逻辑
     */
    public FlyQiuPropertyStatistical(PropertyDescriptor pd, String outFieldName, FlyQiuCalculate<?> calculate) {
        this(new FlyQiuPropertyDesc(pd), outFieldName, calculate);
    }

    /**
     * 使用FlyQiuBaseProperty描述属性初始化统计类
     *
     * @param prop      属性描述对象
     * @param calculate 统计计算逻辑
     */
    public FlyQiuPropertyStatistical(FlyQiuBaseProperty prop, FlyQiuCalculate<?> calculate) {
        this(prop, prop.getName(), calculate);
    }

    /**
     * 使用FlyQiuBaseProperty描述属性和输出字段名称初始化统计类
     *
     * @param prop         属性描述对象
     * @param outFieldName 输出字段名称
     * @param calculate    统计计算逻辑
     */
    public FlyQiuPropertyStatistical(FlyQiuBaseProperty prop, String outFieldName, FlyQiuCalculate<?> calculate) {
        if (prop == null) {
            throw new NullPointerException("field cannot be null ");
        }
        if (calculate == null) {
            throw new NullPointerException("calculate cannot be null");
        }
        this.field = prop;
        if (StringUtils.isBlank(outFieldName)) {
            outFieldName = field.getName();
        }
        this.outFieldName = outFieldName;
        this.calculate = (FlyQiuCalculate<? super Object>) calculate;
    }

    /**
     * 获取属性名称。
     *
     * @return 属性名称
     */
    @Override
    public String getFieldName() {
        return outFieldName;
    }

    /**
     * 获取格式化后的统计值字符串
     * <p>
     * 此方法提供了统计值的格式化功能，包括添加前缀和后缀、以及对数值的精度进行处理
     *
     * @return 格式化后的统计值字符串
     */
    @Override
    public final String getStatisticalValueStr() {

        Object statisticalValue = getStatisticalValue();

        if (!(statisticalValue instanceof Number)) {
            return prefix + statisticalValue + suffix;
        }

        BigDecimal num;
        if (statisticalValue instanceof BigDecimal) {
            num = (BigDecimal) statisticalValue;
        } else {
            num = new BigDecimal(String.valueOf(statisticalValue));
        }

        num = num.setScale(precision, RoundingMode.HALF_UP);

        return prefix + num + suffix;
    }

    /**
     * 获取统计值
     *
     * @return 统计值
     */
    @Override
    public Object getStatisticalValue() {
        return calculate.getValue(this);
    }

    /**
     * 重置统计值
     */
    @Override
    public void resetValue() {
        calculate.restCalculate();
    }

    /**
     * 对给定上下文和行数据进行统计计算
     *
     * @param context 上下文对象
     * @param row     当前行数据
     */
    @Override
    public void calculate(FlyQiuSCContext<?> context, T row) {
        Object value = field.getValue(row); // 获取当前行的属性值
        Object convert = calculate.convert(value); // 将属性值转换为目标类型
        calculate.calculate(context, convert); // 计算新的统计值
    }
}
